#!/usr/bin/python
# -*-coding:utf-8-*-
from gplearn_evolve.functions import make_function

from gplearn_evolve.base.GPFunctionsV1 import *
from gplearn_evolve.base.GPFunctionsV2 import *
from gplearn_evolve.base.GPFunctionsV3 import *
from gplearn_evolve.base.GPFunctionsCWV1 import *
from gplearn_evolve.base.GPFunctionsCWV2 import *

from gplearn_evolve.base.config import self_defined_func_dict
from gplearn_evolve.base.config import gp_define_func_list

def _select_gpfunc_name(gpfunction_candidates, selected_number=None, selected_all=False):
    if selected_all:
        return gpfunction_candidates

    if selected_number is None:
        selected_number = len(gpfunction_candidates) // 2

    if type(gpfunction_candidates) == list:
        assign_prob = 1. / len(gpfunction_candidates)

        gpfunction_candidates_dict = {
           f: assign_prob for f in gpfunction_candidates
        }
    else:
        gpfunction_candidates_dict = gpfunction_candidates

    gpfunction_candidates_df = pd.DataFrame({'prob':list(gpfunction_candidates_dict.values())},
                                            index=list(gpfunction_candidates_dict.keys()))

    gpfunction_candidates_df['prob'] = gpfunction_candidates_df['prob'].cumsum()

    selected_func_list = []
    count = 0
    while True:
        gpfunction_candidates_df['select_prob'] = np.random.uniform(0, 1)

        selected_prob = gpfunction_candidates_df['prob'] - gpfunction_candidates_df['select_prob']

        selected = selected_prob[selected_prob >= 0].index[0]

        if selected in selected_func_list:
            continue

        selected_func_list.append(selected)

        count += 1

        if count >= selected_number:
            break

    return selected_func_list

def get_gp_functions(gpfunction_candidates, selected_number=None, keys=None, neu_keys=None, base=5, key_by_path=True, selected_all=False):
    selected_func_list = _select_gpfunc_name(gpfunction_candidates, selected_number=selected_number, selected_all=selected_all)

    # TODO - 必须的函数
    function_set = [
        'add',
        'mul',
        'div',
        'sub',
        'abs',
        # 'neg',
    ]

    for func_name in selected_func_list:
        if func_name in gp_define_func_list:
            function_set.append(func_name)
            continue

        if func_name.startswith('cs_') or func_name.startswith('ts_cw_') or func_name.startswith('cw_'):
            if 'neu' in func_name:
                func = eval('def_keyfunc_'+func_name)(keys, neu_keys, key_by_path=key_by_path)
            else:
                func = eval('def_keyfunc_'+func_name)(keys, key_by_path=key_by_path)
        elif func_name.startswith('ts_') and (not func_name.startswith('ts_cw_')):
            func = eval('def_keyfunc_'+func_name)(keys, base, key_by_path=key_by_path)
        else:
            func = eval('def_'+func_name)

        this_arity = self_defined_func_dict[func_name]
        # this_arity = self_defined_seq_func_dict[func_name]

        make_func = make_function(function=func, name=func_name, arity=this_arity)

        function_set.append(make_func)

    return function_set


